/*
 * project1.c
 *
 *  Created on: Sep 5, 2011
 *      Author: Juan Pablo Calderin
 */

#define _GNU_SOURCE	//	to use the GNU extensions (asprintf)

#include <stdio.h>		// for standard library functions
#include <stdlib.h>
#include <unistd.h>
#include <string.h>		// for string parsing
#include <ctype.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/wait.h>	// for wait function

/* Global Variables */

int size = 80;	// buffer size
char hostname[128];
char str[80];		// entire typed command
char * justcmd;	// contains only the command name, e.g. "ls"
char * full_path = NULL;	// start with an empty string
char * arguments[128];	// pointer that should point to a character array
char pwd[128];	// to store and update PWD

// ------------------------------------------------------------------------- //

/*Function prototypes in a somewhat order of run*/
void readline(char *, int);	// read user input
void getcmd();	// get the first argument (the command)
void getArgs();	// tokenize the arguments
void prompt();	// display the prompt
int check(char *, char *);	// check that the 
void executecmd(char *);	// executes the command using execv
int parsePath(char *);	// parse the PATH environment variable, returns 1 if successful
void cd();	// implements the "cd" command
void echo();	// implements the "echo" command
int BuiltIn(char * cmd);	// checks if the command is built-in
void exitcmd();	// implements the "exit" command
void which(char *);
void viewproc();	// implements viewproc built-in command

// ------------------------------------------------------------------------- //

void readline(char *line, int size)	// read user input into a string
{
   if ( fgets(line, size, stdin) ) {		// get standard input
	   char *newline = strchr(line, '\n'); // locate null character
	   if ( newline )
		   *newline =  '\0'; // get rid of null character
   }
   
}	// end of readline

// ------------------------------------------------------------------------- //

void getcmd(){	// gets the raw command, without any arguments
	char * temp = strdup(str);	// to preserve original str
	justcmd = strtok (temp, " ");	// get the first word (command)

}	// end of getcmd

// ------------------------------------------------------------------------- //

void getArgs() {	//  gets the arguments into the arguments array of pointers

	int i=0;	// index variable
	
	//~ if (full_path!=NULL)	// check that the path has been set
		//~ arguments[i]=full_path;		// get first argument
		// since the path was not set, then set the first argument to the cmd
		arguments[i]=justcmd;		// get first argument

	while (arguments[i] != NULL)	// get the rest of the arguments
		arguments [++i]=strtok (NULL, " ");	// get "words" (separated with spaces)

	arguments[++i]=NULL;	// don't forget about the NULL at the end
	
}	// end of getArgs

// ------------------------------------------------------------------------- //


void prompt(){	// display prompt to the user

	hostname[127] = '\0';	// add null character to the end
	gethostname(hostname, 128);	// store host name into char
	
	printf("%s@%s:%s(-_-)",getenv("USER"), hostname, pwd );	// show prompt
	readline(str, sizeof (str));	// read line from user
	//printf("You typed %s.\n", str);
	getcmd();	// get the actual command without arguments
	//printf("Just the command is %s\n", justcmd);
	//parsePath();	// parse the PATH environment variable

}	// end of prompt

// ------------------------------------------------------------------------- //
 
void cd() {		// implements built-in "cd" commmand
	
	if (arguments[1] != NULL) {	// to prevent the segmentation fault
		if(chdir(arguments[1])!=0)		// check if it changed directory
			printf("%s: No such file or directory.\n",arguments[1]);
	}
	else	// change to home folder if cd doesn't have any arguments
		chdir(getenv("HOME"));	// change directory to home directory	
	
}

// ------------------------------------------------------------------------- //

void echo() {	// implements a simple built-in "echo" commmand
	int i;	// index variable
	for(i=1;arguments[i]!=NULL;i++){	// check if there are words to print

		if(strchr(arguments[i],'$')) {	// check for environment variables
			
			char * atemp = strtok (arguments[i], "$");	// get the variable name
			
			if(!getenv(atemp)) {	// check that the variable exists
				printf("\nSorry \"%s\" is not a valid environment variable.",atemp);
				break;	// get out of the loop and stop checking
			}
				
			arguments[i]=malloc(strlen(getenv(atemp)));	// allocate space
			strcpy(arguments[i],getenv(atemp));	// copy the variable
		}
		
		if(i==1)	// spacing for first word
			printf("%s",arguments[i]);
		else		// spacing for the rest of the words
			printf(" %s",arguments[i]);
		
	
	}
	
	printf("\n");	// add the \n at the end
	
	
}	// end of echo

// ------------------------------------------------------------------------- //

void exitcmd() {
	
	
	
}


void which(char * path) {
	if (arguments[1] == NULL)	// called which without any arguments
		puts("Which what?");
	else if (!strcmp(arguments[1],"exit") || !strcmp(arguments[1],"cd")
			|| !strcmp(arguments[1],"history") || !strcmp(arguments[1],"echo")
			|| !strcmp(arguments[1],"viewproc") )
		printf("%s: shell built-in command.\n",arguments[1]);	// built-in cmd
	else if (parsePath(arguments[1]))
		printf("\n%s\n",full_path);		// print full path 
	else
		printf("%s: not found inside the path.\n",arguments[1]);

	
}
// ------------------------------------------------------------------------- //

int check(char *file_path, char *cmd) {	// check that the file (command) exists

	asprintf(&full_path, "%s/%s", file_path, cmd);	// to concat strings and alloc memory

	//printf("The path that i am checking is %s\n", full_path);
	if( access( full_path, F_OK ) != -1 )	// check file for existence
		return 1;	// file was found
	return 0;	// file doesn't exist

}	// end of check

// ------------------------------------------------------------------------- //

void executecmd(char *path) {	// execute command since it was found

	int status;
	pid_t pid;
	
	//printf("Inside executecdm\n");
	if ((pid = fork()) == 0 )
			execv(path, arguments);
	else
		waitpid(-1, &status, 0);
}


int parsePath(char *cmd) {		// parses PATH environment variable

	char *path = strdup(getenv("PATH"));
	const char *const delim = ":";

	// For fully correct results, you must allocate the save_pointer
	// to be the length of the string you'll parse.
	char *save = (char *) malloc(strlen(path));

	// Here, we save the position of the save pointer so that we can free
	// it later.
	char *path_start_addr = path;	// start memory address for path pointer
	char *save_start_addr = save;	// start memory address for save pointer
	char *token; 		// useful string after tokenization
	
	//getArgs();	// get the arguments

	// Iterate through each token	
	for(token = strtok_r(path, delim, &save); token != NULL; token = strtok_r(NULL, delim, &save)){
		if(check(token, cmd)) {
			return 1;	// it found something
			break;
		}
	}
	
	// free memory
	free(save_start_addr);
	free(path_start_addr);
	return 0;	// didn't find anything
	
}	// end of parsePath

int BuiltIn(char * cmd) {	// returns 1 if built in command was found, 0 if not
	
	if(!strcmp(cmd,"echo")) {
		puts("Inside echo");
		echo();	// call echo
		return 1;
	}
	else if(!strcmp(cmd,"cd")) {
		puts("Inside cd");
		cd();	// call cd
		return 1;
	}
	
	else if(!strcmp(cmd,"viewproc")) {
		puts("Inside viewproc");
		viewproc();	// call viewproc
		return 1;
	}
	
	
	
	puts("Exiting built-in");
	return 0;	// not a built-in command

}


void viewproc() {	// implements viewproc built-in command
	int c;	// to contain the character
	if (check("/home/class/calderin",arguments[1]) )  { // check if file exists
		printf("%s was found in /procs.\n",arguments[1]);
		FILE *myfile;	// create FILE object
		myfile=fopen(full_path,"r");	// open file for reading
		while(c != EOF) {
			//puts("Getting character");
			c= fgetc (myfile);
			if (c != EOF)	// print every character except for EOF (int)
				printf("%c",c);	// print character
		}
		fclose(myfile);	// close file
		printf("\n");
	}
	else
		printf("error: %s is not in /procs.\n",arguments[1]);
}	// end of viewproc


// ------------------------------------------------------------------------- //

int main(void)
{
	while(1) {
		getcwd(pwd,127);	// update current working directory
		prompt();		// call our prompt
		
		
		getArgs();		// get arguments
		if(!strcmp(justcmd,"exit")) {	// exit the shell
			puts("Exiting the shell");
			return 0;
		}
		
		else if (BuiltIn(justcmd));	// check and run if built-in
		
		else if(parsePath(justcmd)) 	{	// path was found
			printf("PAth was found, it was %s!\n\n", full_path);
			
			if(!strcmp(justcmd,"which") )
				which(full_path);	// if the command was "which"
			else
				executecmd(full_path);	// execute command with the path
		}
		
		else	// if nothing else worked
			printf("%s: command not found.\n",justcmd);
	}
	
   return 0;
}
